
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

#define  LINE_SIZE 1024
#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
#define ARGV_SIZE 32
#define EXIT_CODE 44

//全局变量
extern char** environ;
int lastcode = 0;
char commandline[LINE_SIZE];  //大字符串
char* argv[ARGV_SIZE];        //小字符串数组
char pwd[LINE_SIZE];
char myenv[LINE_SIZE];      // 自定义环境变量表

void getpwd()
{
	getcwd(pwd, sizeof(pwd));
}

const char* getusername()
{
	return getenv("USER");
}

const char* gethostname()
{
	return getenv("HOSTNAME");
}



//1. 打印 bash，将输入的内容，包装成一个大字符串，“大字符串”--------输出型参数
void interact(char* cline, int size)
{
	getpwd();
	printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname(),pwd);
	char* s = fgets(cline, size, stdin);
	//scanf("%s", cline);  scanf遇空格停止读取
	assert(s);
	(void)s;
	// "abc\n\0"
	cline[strlen(cline) - 1] = '\0';
}

//2. 将一个“大字符串”-----》小字符串 数组，“小字符串 数组”--------输出型参数
int splitstring(char* cline, char* _argv[])
{
	int i = 0;
	argv[i++] = strtok(cline, DELIM);
	while (_argv[i++] = strtok(NULL, DELIM)); // 故意写的=
	return i - 1;
}


//3. 将字符串数组当作指令去执行
//3.1 被当作普通指令------shell父进程fork一个子进程去执行
void NormalExcute(char* _argv[])
{
	pid_t id = fork();
	if (id < 0)
	{
		perror("fork");
		return;
	}
	else if (id == 0) //child
	{
		execvp(_argv[0], _argv);
		exit(EXIT_CODE);
	}
	else
	{
		int status = 0;
		pid_t rid = waitpid(id, &status, 0);
		if (id == rid)
		{
			lastcode = WEXITSTATUS(status);
		}
	}
}


//3.2 被当作内建指令------由shell父进程自己去执行
//cd ../.
//export a=111111
//echo $?
//echo $a
//echo abc
int buildCommand(char* _argv[], int _argc)
{
	if (_argc == 2 && strcmp(_argv[0], "cd") == 0)
	{
		chdir(argv[1]);
		getpwd();  //重新改变路径，并更新
	    sprintf(getenv("PWD"), "%s", pwd); //打印路径
		//printf("%s", pwd);
		return 1;
	}
	else if (_argc == 2 && strcmp(_argv[0], "export") == 0)
	{
		strcpy(myenv, _argv[1]);
		putenv(myenv);     //将新添的环境变量加入环境变量表中
		return 1;
	}
	else if(_argc == 2 && strcmp(_argv[0], "echo") == 0)
	{
		if (strcmp(_argv[1], "$?") == 0)
		{
			printf("%d\n", lastcode);
			lastcode = 0;
		}
		else if (*_argv[1] == '$')
		{
			char* val = getenv(_argv[1] + 1);
			if (val) printf("%s\n", val);
		}
		else
		{
			printf("%s\n", _argv[1]);
		}
		return 1;
	}

	// 特殊处理一下ls
	if (strcmp(_argv[0], "ls") == 0)
	{
		_argv[_argc++] = "--color";
		_argv[_argc] = NULL;
	}
	return 0;
}



int main()
{
	while (1)
	{
		//1. 交互问题，获取命令行
		interact(commandline, sizeof(commandline));    //大字符串已得到


		//2.子串分割的问题，解析命令行
		int argc = splitstring(commandline, argv);
		if (!argc)     continue;                      //大字符串已被切割，若没有切割出任何东西，则continue

		//3. 指令的判断、执行
		int n = buildCommand(argv, argc);
		if (!n) NormalExcute(argv);
	}

	return 0;
}

